import gradio as gr
import cv2
import numpy as np

import re

def parse_css_value(value, reference=1000):
    """Parse CSS values (px, vh, %) into numeric pixels"""
    if isinstance(value, (int, float)):
        return value
        
    if isinstance(value, str):
        # Match numeric part and unit
        match = re.match(r'([\d.]+)(px|vh|%)?', value)
        if match:
            num = float(match.group(1))
            unit = match.group(2)
            
            if unit == 'px':
                return num
            elif unit == 'vh':
                # Approximate vh as percentage of reference height (typical screen height)
                return num * reference / 100
            elif unit == '%':
                # Use percentage of reference width (for width) or height (for height)
                return num * reference / 100
                
    # Fallback to default if parsing fails
    return reference


def point_handler(evt: gr.SelectData, 
                marked_img, 
                clean_img, 
                points_list,
                boxes_list,
                last_action):
    
    print(f"points.py==>Point handler called - last_action: {last_action}")
    print(f"points.py==>Initial points type: {type(points_list)}")
    if not isinstance(points_list, list):
        print(f"points.py==>ERROR: points is not a list! Value: {points_list}")
        points_list = []
    print(f"points.py==>Initial points count: {len(points_list)}")
    
    # Always return current values if clean_img is missing or in none mode
    if clean_img is None or last_action == "none":
        print("points.py==>No clean image available or in none mode")
        return [marked_img, clean_img, points_list, last_action]
        
    # Start from pristine clean image
    working_img = clean_img.copy()
    print("points.py==>Starting fresh from clean image")
    
    # Check if evt is valid before accessing its properties
    if evt is None:
        print("points.py==>Event object is None")
        return [working_img, clean_img, points_list, last_action]
        
    # More robust event data checking
    if not hasattr(evt, '_data'):
        print("points.py==>Event missing _data attribute")
        return [working_img, clean_img, points_list, last_action]
        
    if evt._data is None:
        print("points.py==>Event data is None")
        return [working_img, clean_img, points_list, last_action]
        
    # Handle case where index might be in _data directly
    if not hasattr(evt, 'index') and 'index' not in evt._data:
        print("points.py==>Event missing index data")
        return [working_img, clean_img, points_list, last_action]
        
    # Get index from either evt.index or evt._data['index']
    index = evt.index if hasattr(evt, 'index') else evt._data.get('index')
    if index is None:
        print("points.py==>Could not determine click coordinates")
        return [working_img, clean_img, points_list, last_action]
        
    print(f"points.py==>index: {index}")
    
    try:
        # Debug event data structure
        print(f"points.py==>Original event type: {type(evt)}")
        print(f"points.py==>Original event data: {evt._data if hasattr(evt, '_data') else None}")
        
        # Convert display coordinates
        orig_x, orig_y = evt.index[0], evt.index[1]
        if orig_x is None or orig_y is None:
            print("points.py==>Coordinate conversion failed")
            return [working_img, clean_img, points_list, last_action]
        
        # Process the point based on last_action
        if last_action == "add":
            print(f"points.py==>Adding point at: ({orig_x}, {orig_y})")
            points_list.append((orig_x, orig_y))
        elif last_action == "remove" and points_list:
            print(f"points.py==>Looking to remove point near: ({orig_x}, {orig_y})")
            
            # Find nearest point
            nearest_idx = min(
                range(len(points_list)),
                key=lambda i: (points_list[i][0]-orig_x)**2 + (points_list[i][1]-orig_y)**2
            )
            print(f"points.py==>Removing point at index {nearest_idx}: {points_list[nearest_idx]}")
            points_list.pop(nearest_idx)
        
        # Redraw all points on the image
        for pt in points_list:
            # Draw points using original coordinates (no zoom scaling)
            cv2.drawMarker(
                working_img, 
                (pt[0], pt[1]), 
                (0,0,255), 
                cv2.MARKER_STAR, 
                20, 
                2
            )
        
        print(f"points.py==>Final points count: {len(points_list)}")
        return [working_img, clean_img, points_list, last_action]
        
    except Exception as e:
        print(f"Error in point_handler: {str(e)}")
        return [working_img, clean_img, points_list, last_action]


# Return clean image and empty points list
def clear_points_handler(clean_img):
    return clean_img, []
